<?php
	
	namespace Aspic;
	
	use Aspic as A;
	
	/**
	* Propose possibility to create code text and indent it, making new lines, etc.
	*/
	class CodeBuilder {
		
		protected $_indentString;
		
		protected $_code;
		protected $_currentIndent;
		
		/**
		* @var bool When contents is added with a prefix / suffix (next() method), when contents == "", prefix / suffix will not be append to the code
		*/
		protected $_notAddPrefixSuffixWhenEmpty;
		
		public function __construct($code = '', $notAddPrefixSuffixWhenEmpty = true, $indentString = '	') {
			$this->_indentString = $indentString;
			$this->_notAddPrefixSuffixWhenEmpty = $notAddPrefixSuffixWhenEmpty;
			
			$this->_code = $this->_formatRawCode($code);
			$this->_currentIndent = 0;
		}
		
		public function clear() {
			$this->_code = '';
		}
		
		/**
		* Intelligent remove begining $indentString and set lines to current indent
		* First line is not considered as normal line
		*/
		protected function _formatRawCode($code) {
			return static::formatRawCode($code, $this->_currentIndent, $this->_indentString);
		}
		
		protected function _getNumberOfStartingIndent($line) {
			return static::getNumberOfStartingIndent($line, $this->_indentString);
		}
		
		/**
		* Intelligent remove begining $indentString and set lines to current indent
		* First line is not considered as normal line
		*/
		public static function formatRawCode($code, $indent = 0, $indentString = '	') {
			$code = str_replace("\r", '', $code);
			$lines = explode("\n", $code);
			// var_dump($lines);
			$arrNbIndents = array();
			
			$firstLinePassed = false;
			$firstLineIndex = 0;
			
			// We do not consider first not empty line as normal line to test for tab at the begining
			for($i = 0; $i < count($lines); ++$i) {
				if(trim($lines[$i]) != '') {
					if($firstLinePassed) {
						$arrNbIndents[$i] = static::getNumberOfStartingIndent($lines[$i], $indentString);
					}
					else {
						$firstLinePassed = true;
						$firstLineIndex = $i;
					}
				}
				elseif(!$firstLinePassed) {
					$firstLinePassed = true;
					$firstLineIndex = $i;
				}
			}
			
			$min = (count($arrNbIndents) > 0)?min($arrNbIndents):0;
			
			$lines[$firstLineIndex] = str_repeat($indentString, $indent).$lines[$firstLineIndex];
			
			for($i = $firstLineIndex + 1; $i < count($lines); ++$i) {
				$lines[$i] = substr($lines[$i], $min * strlen($indentString));
				$lines[$i] = str_repeat($indentString, $indent).$lines[$i];
				$lines[$i] = rtrim($lines[$i]);
			}
			
			$code = implode("\n", $lines);
			
			return $code;
		}
		
		public static function getNumberOfStartingIndent($line, $indentString = '	') {
			$currPos = 0;
			$number = 0;
			
			$currPos = strpos($line, $indentString);
			
			while($currPos !== false) {
				$number++;
				$currPos = strpos($line, $indentString, ($currPos + strlen($indentString)));
			}
			
			return $number;
		}
		
		/**
		* Change current indentation (relatively) for next time code is added
		*/
		public function indent($level = 1) {
			if($this->_currentIndent + $level < 0) {
				throw new Exception('Cannot set indent below 0 ('.$level.')');
			}
			
			$this->_currentIndent += $level;
		}
		
		public function revIndent($level = 1) {
			$this->indent(-$level);
		}
		
		public function setIndent($level) {
			$this->_currentIndent = $level;
		}
		
		public function getIndent() {
			return $this->_currentIndent;
		}
		
		/**
		* New line
		*/
		public function nl($code, $indent = null) {
			$this->next($code, $indent, "\n");
			
			return $this;
		}
		
		/**
		* Append some new line to the code
		*/
		public function nlRepeat($nbRepetitions) {
			$this->next(str_repeat("\n", $nbRepetitions));
		}
		
		/**
		* Paragraph
		*/
		public function par($code, $indent = null) {
			$this->next($code, $indent, "\n\n");
			
			return $this;
		}
		
		/**
		* @param integer $indent Modify the indent relatively only for this new portion of code
		*/
		public function next($code, $indent = null, $prefix = '', $suffix = '') {
			if(strlen($code) > 0) {
				if(!is_null($indent)) {
					$this->indent($indent);
				}
				
				if($code instanceof self) {
					$code = $code->out();
				}
				
				$parsedCode = $this->_formatRawCode($code, $indent);
				
				if(!is_null($indent)) {
					$this->indent(-$indent);
				}
				
				$parsedCode = $prefix.$parsedCode.$suffix;
				
				$this->_code .= $parsedCode;
				
			}
			
			return $this;
		}
		
		public function nlTrim($code, $indent = null) {
			return $this->nl(trim($code), $indent);
		}
		
		public function parTrim($code, $indent = null) {
			return $this->par(trim($code), $indent);
		}
		
		/**
		 * Same as next but use trim() on code before
		 */
		public function nextTrim($code, $indent = null) {
			return $this->next(trim($code), $indent);
		}
		
		public function out() {
			return $this->_code;
		}
		
		public function __toString() {
			return $this->out();
		}
		
	}	
	
?>